增加应用程序和第三方库¶
Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.
概述¶
本文档指导用户在Yocto构建系统中添加自定义应用程序和第三方库(以helloworld为例)。通过本文档的步骤,您可以:
将自定义代码集成到Yocto项目中
生成 IPK 安装包
编译和使用第三方库文件
将组件集成到系统镜像中
前置准备¶
环境要求¶
已安装必要的开发工具包
已克隆Yocto项目代码
熟悉基本的C语言编程
了解Makefile和BitBake基础
项目目录结构¶
创建自定义 meta 层,推荐使用以下目录结构:
meta-custom/
├── conf/
│ └── layer.conf # 层配置文件
├── recipes-apps/ # 应用程序配方目录
│ └── helloworld/
│ ├── files/
│ │ ├── helloworld.c # 应用源码
│ │ ├── Makefile # 编译脚本
│ │ └── helloworld.service # systemd服务文件
│ └── helloworld_1.0.bb # BitBake配方
└── recipes-libs/ # 库配方目录
└── lib-helloworld/
├── files/
│ ├── lib_helloworld.c # 库源码
│ ├── lib_helloworld.h # 库头文件
│ └── Makefile # 编译脚本
└── lib-helloworld_1.0.bb # BitBake配方
添加自定义应用程序¶
创建层目录结构¶
创建目录¶
mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-apps/helloworld/files
mkdir -p ${YOCTO_DIR}/layers/meta-custom/conf
配置自定义层¶
在 layers/meta-custom/conf/layer.conf 文件中添加:
BBPATH .= ":${LAYERDIR}"
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb"
LAYERDEPENDS_meta-custom = "core"
LAYERSERIES_COMPAT_meta-custom = "kirkstone"
注册层到构建系统¶
编辑 layers/meta-qcom-distro/conf/bblayers.conf,添加自定义层路径:
BBLAYERS = " \
${WORKSPACE}/layers/meta-custom \
"
添加到镜像配方¶
编辑 layers/meta-quectel/recipes-products/images/qcom-multimedia-image.bbappend,添加:
# 添加应用程序
IMAGE_INSTALL:append = " helloworld"
# 添加库文件(如果需要)
IMAGE_INSTALL:append = " \
lib-helloworld \
lib-helloworld-dev \
lib-helloworld-staticdev \
"
创建应用程序源码¶
编写主程序¶
在 meta-custom/recipes-apps/helloworld/files/ 目录下创建 helloworld.c:
// helloworld.c#include <stdio.h>int main() {printf("Hello, world!\n");return 0;}
编写 Makefile¶
在 meta-custom/recipes-apps/helloworld/files/ 目录下创建 Makefile:
# 使用Yocto提供的编译标志all: helloworld
helloworld: helloworld.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<clean:
rm -f helloworld
.PHONY: all clean
创建systemd服务文件¶
在 meta-custom/recipes-apps/helloworld/files/ 目录下创建 helloworld.service:
[Unit]Description=Hello World Service[Service]Type=simpleExecStart=/usr/bin/helloworld[Install]WantedBy=multi-user.target
编写 BitBake 配方¶
在 meta-custom/recipes-apps/helloworld/ 目录下创建 helloworld_1.0.bb:
SUMMARY = "Hello World application"
DESCRIPTION = "A simple Hello World application."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"# 源代码文件列表
SRC_URI = "file://helloworld.c \
file://Makefile \
file://helloworld.service"
# 设置工作目录
S = "${WORKDIR}"# 继承 systemd 类
inherit systemd
# 定义 systemd 服务文件名
SYSTEMD_SERVICE:${PN} = "helloworld.service"# 配置阶段(跳过)
do_configure() {
bbnote "skip do_configure"}# 编译阶段
do_compile() {# 先清理
oe_runmake -f ${S}/Makefile clean
# 执行编译
oe_runmake -f ${S}/Makefile
}# 安装阶段
do_install() {# 创建可执行文件安装目录
install -d ${D}${bindir}# 安装可执行文件
install -m 0755 ${S}/helloworld ${D}${bindir}# 创建 systemd 服务目录
install -d ${D}${systemd_system_unitdir}# 安装服务文件
install -m 0644 ${S}/helloworld.service ${D}${systemd_system_unitdir}}
构建和安装应用程序¶
配置构建环境¶
进入代码工作目录,执行:
source quectel_build/compile/build.sh
方式一:单独编译并安装IPK包¶
步骤 1:编译应用程序
bitbake helloworld
步骤 2:查找生成的IPK包
find ~/build-qcom-wayland/tmp-glibc/deploy -name "helloworld*.ipk"
输出示例:
/home/quectel/build-qcom-wayland/tmp-glibc/deploy/ipk/armv8-2a/helloworld_1.0-r0_armv8-2a.ipk
步骤 3:推送到设备
adb push helloworld_1.0-r0_armv8-2a.ipk /mnt
步骤 4:安装IPK包
在设备上执行:
# 挂载文件系统为读写模式mount -o remount,rw /usr
# 安装 IPK 包
opkg install /mnt/helloworld_1.0-r0_armv8-2a.ipk
步骤 5:验证安装
/usr/bin/helloworld
预期输出:
Hello, world!
方式二:整编系统镜像¶
步骤 1:配置构建参数
# 用户可自定义输入参数,从提示的有效选项中选择
buildconfig QSM200UPEMPI QSM200UPEMNARL1A01_BL01BP01K0M01V01_LP5.15.153.01.001_V01 STD
步骤 2:编译整个系统
buildall
步骤 3:打包镜像
buildpackage
步骤 4:烧录镜像
生成的镜像位于 quectel_build/fw/QSM200UPEMNARL1A01_BL01BP01K0M01V01_LP5.15.153.01.001_V01 路径下。
步骤 5:验证安装
烧录完成后,在设备上执行:
/usr/bin/helloworld
预期输出:
Hello, world!
添加第三方库¶
创建库文件¶
创建目录¶
mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-libs/lib-helloworld/files
创建库头文件¶
在 meta-custom/recipes-libs/lib-helloworld/files/ 目录下创建 lib_helloworld.h:
// lib_helloworld.h#ifndef LIB_HELLO_H#define LIB_HELLO_Hvoid hello_print(const char* name);#endif
创建库源码¶
在 meta-custom/recipes-libs/lib-helloworld/files/ 目录下创建 lib_helloworld.c:
// lib_helloworld.c#include <stdio.h>#include "lib_helloworld.h"void hello_print(const char* name) {printf("[LIB] Hello, %s!\n", name);}
编写Makefile¶
在 meta-custom/recipes-libs/lib-helloworld/files/ 目录下创建 Makefile:
LIB_VERSION = 1.0
all:${CC} ${CFLAGS} -fPIC -c lib_helloworld.c -o lib_helloworld.o
${CC} ${CFLAGS} -fPIC -shared lib_helloworld.o -o libhello.so.${LIB_VERSION} \
-Wl,-soname,libhello.so.1 ${LDFLAGS}
ar rcs libhello.a lib_helloworld.o
ln -sf libhello.so.${LIB_VERSION} libhello.so
ln -sf libhello.so.${LIB_VERSION} libhello.so.1
clean:
rm -f libhello.so* lib_helloworld.o libhello.a
编写 BitBake 配方¶
在 meta-custom/recipes-libs/lib-helloworld/ 目录下创建 lib-helloworld_1.0.bb:
SUMMARY = "Hello World Library"
SECTION = "libs"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://lib_helloworld.c \
file://lib_helloworld.h \
file://Makefile"
S = "${WORKDIR}"
EXTRA_OEMAKE = "'CC=${CC}' 'CFLAGS=${CFLAGS} -fPIC' 'LDFLAGS=${LDFLAGS}'"
do_install() {# 创建目标目录
install -d ${D}${libdir}
install -d ${D}${includedir}# 安装共享库及符号链接
install -m 0755 ${S}/libhello.so.${PV} ${D}${libdir}/
ln -sf libhello.so.${PV} ${D}${libdir}/libhello.so
ln -sf libhello.so.${PV} ${D}${libdir}/libhello.so.1# 安装静态库
install -m 0644 ${S}/libhello.a ${D}${libdir}/# 安装头文件
install -m 0644 ${S}/lib_helloworld.h ${D}${includedir}/}# 包文件分配
FILES:${PN} = " \
${libdir}/libhello.so.1 \
${libdir}/libhello.so.${PV} \
"
FILES:${PN}-dev = " \
${libdir}/libhello.so \
${includedir}/lib_helloworld.h \
"
FILES:${PN}-staticdev = " \
${libdir}/libhello.a \
"
构建和使用第三方库¶
配置构建环境¶
source quectel_build/compile/build.sh
方式一:单独编译并安装¶
步骤 1:编译库
bitbake lib-helloworld
步骤 2:查找生成的库文件
find ./* -name "libhello*"
步骤 3:推送到设备
adb push libhello.so libhello.so.1 libhello.so.1.0 libhello.a /usr/lib
步骤 4:修复权限和符号链接
在设备上执行:
# 设置执行权限chmod 755 /usr/lib/libhello.so*
chmod 644 /usr/lib/libhello.a
# 重建符号链接cd /usr/lib
ln -sf libhello.so.1.0 libhello.so.1
ln -sf libhello.so.1 libhello.so
步骤 5:编写测试程序
创建静态库测试程序 static_test_helloworld.c:
#include <stdio.h>void hello_print(const char*);int main() {printf("static lib test\n");hello_print("world");return 0;}
创建动态库测试程序 dynamic_test_helloworld.c:
#include <stdio.h>void hello_print(const char*);int main() {printf("dynamic lib test\n");hello_print("world");return 0;}
步骤 6:编译并运行测试程序
静态库测试:
gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello
预期输出:
static lib test
[LIB] Hello, world!
动态库测试:
gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello
预期输出:
dynamic lib test
[LIB] Hello, world!
方式二:整编系统镜像¶
验证库文件
烧录完成后,在设备上检查库文件:
ls -l /usr/lib/libhello*
预期输出:
-rw-r--r--. 2 root root 5002 Jan 1 1970 /usr/lib/libhello.a
lrwxrwxrwx. 1 root root 15 Jul 25 06:26 /usr/lib/libhello.so -> libhello.so.1.0
lrwxrwxrwx. 1 root root 15 Jul 25 06:26 /usr/lib/libhello.so.1 -> libhello.so.1.0
-rwxr-xr-x. 2 root root 5968 Jan 1 1970 /usr/lib/libhello.so.1.0
运行测试程序
静态库测试:
gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello
预期输出:
static lib test
[LIB] Hello, world!
动态库测试:
gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello
预期输出:
dynamic lib test
[LIB] Hello, world!
注意事项¶
层优先级:确保自定义层的配置正确,避免与其他层冲突
许可证声明:务必正确设置LICENSE和LIC_FILES_CHKSUM
文件权限:注意可执行文件和库文件的权限设置
符号链接:动态库的符号链接必须正确设置
依赖关系:如果库依赖其他包,需在配方中声明DEPENDS
版本管理:建议使用版本号管理库文件
总结¶
通过本文档的指导,您可以在Yocto环境中:
创建自定义应用程序和库
生成 IPK 安装包进行单独部署
将组件集成到系统镜像中
验证和测试自定义组件
掌握这些技能后,您可以根据实际需求扩展和定制系统功能。